#include "tcpclient.h"

#include <QDataStream>

TcpClient::TcpClient(QObject *parent) : QObject(parent)
{

}

void TcpClient::init()
{
    m_socket = new QTcpSocket;
    m_filesocket = new QTcpSocket;
    m_payloadSize=64*1024;
    m_recvtotalBytes=0;
    m_recvbytesReceived=0;
    m_recvfileNameSize=0;
    connect(m_filesocket,SIGNAL(bytesWritten(qint64)),this,SLOT(updateClientProgress(qint64)));
    connect(m_filesocket,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(displayError(QAbstractSocket::SocketError)));
    connect(m_filesocket,SIGNAL(readyRead()),this,SLOT(updateServerProgress()));

    QObject::connect(m_socket,SIGNAL(connected()),this,SLOT(slotConnected()));
    QObject::connect(m_socket,SIGNAL(disconnected()),this,SLOT(slotDisconnected()));
    QObject::connect(m_socket,SIGNAL(readyRead()),this,SLOT(slotReadyRead()));
    QObject::connect(m_socket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error), this, &TcpClient::displayError);
}
void TcpClient::updateServerProgress(){
    QDataStream in(m_filesocket);
    in.setVersion(QDataStream::Qt_5_7);
    // 如果接收到的数据小于16个字节，保存到来的文件头结构
    if (m_recvbytesReceived<=sizeof(qint64)*2){
        if((m_filesocket->bytesAvailable()>=sizeof(qint64)*2)&&(m_recvfileNameSize==0)){
            // 接收数据总大小信息和文件名大小信息
            in>>m_recvtotalBytes>>m_recvfileNameSize;
            m_recvbytesReceived +=sizeof(qint64)*2;
        }
        if((m_filesocket->bytesAvailable()>=m_recvfileNameSize)&&(m_recvfileNameSize!=0)){
            // 接收文件名，并建立文件
            in>>m_recvfileName;
            qDebug()<<tr("recv file %1").arg(m_recvfileName);
            m_recvbytesReceived+=m_recvfileNameSize;
            m_recvlocalFile = new QFile(m_recvfileName);
            if (!m_recvlocalFile->open(QFile::WriteOnly)){
                qDebug() << "server: open file error!";
                return;
            }
        }
        else{
            return;
        }
    }
    // 如果接收的数据小于总数据，那么写入文件
    if(m_recvbytesReceived<m_recvtotalBytes) {
        m_recvbytesReceived+=m_filesocket->bytesAvailable();
        m_recvinBlock = m_filesocket->readAll();
        m_recvlocalFile->write(m_recvinBlock);
        m_recvinBlock.resize(0);
        qDebug()<<tr("recv file %1").arg(m_recvfileName);
    }

    // 接收数据完成时
    if (m_recvbytesReceived==m_recvtotalBytes){
        //m_filesocket->close();
        m_recvlocalFile->close();
        m_recvtotalBytes=0;
        m_recvbytesReceived=0;
        m_recvfileNameSize=0;
        qDebug()<<tr("recv file %1 sucess").arg(m_recvfileName);
    }
}
void TcpClient::displayError(QAbstractSocket::SocketError){
    qDebug()<<m_filesocket->errorString();
    m_filesocket->close();
}


void TcpClient::clientConnectTo(QString ip, QString port)
{
    m_socket->connectToHost(ip,port.toInt());
    m_filesocket->connectToHost(ip,6666);
}

void TcpClient::sendData(const QString & data)
{
    TcpProtocol tcpProtocol;
    tcpProtocol.data = data.toUtf8().data();
    QByteArray arr = tcpProtocol.getStream();
    //for(int i =0 ;i <100 ;i++){
        m_socket->write(arr);
        m_socket->waitForBytesWritten();
    //}
}


void TcpClient::closeConnect()
{
    m_socket->close();
}

void TcpClient::slotReadyRead()
{
    //缓冲区没有数据，直接无视
    if( m_socket->bytesAvailable() <= 0 )
    {
        return;
    }

    //临时获得从缓存区取出来的数据，但是不确定每次取出来的是多少。
    QByteArray buffer;
    //如果是信号readyRead触发的，使用readAll时会一次把这一次可用的数据全总读取出来
    //所以使用while(m_tcpClient->bytesAvailable())意义不大，其实只执行一次。
    buffer = m_socket->readAll();

    //上次缓存加上这次数据

    m_buffer.append(buffer);
    uint32_t size ;
    int totalLen = m_buffer.size();

    while( totalLen )
    {
        //与QDataStream绑定，方便操作。
        QDataStream packet(m_buffer);

        //不够包头的数据直接就不处理。
        if( totalLen < OtherSize )
        {
            break;
        }
        packet >> size ;
        //如果不够长度等够了在来解析
        if( totalLen < size )
        {
            break;
        }
        TcpProtocol p(m_buffer);
        if(p.cmd == 1){
            signReadyRead(p);
        }else if(p.cmd == 2){
            save2File(p);
            if(p.isLastPack != 1)
                sendReadFileData(p);
        }else if(p.cmd == 3){
            if(p.seek >= m_file.size())
                return;
            sendFile(m_filePath,p.seek);
        }else if(p.cmd == 4){
            m_saveFileName = p.data;
            sendReadFileData(p , -1);
        }

        //缓存多余的数据
        buffer = m_buffer.right(totalLen - size);

        //更新长度
        totalLen = buffer.size();
        if(buffer.size() != 0){
            qDebug()<<"黏包";
        }
        //更新多余数据
        m_buffer = buffer;
    }

}

void TcpClient::slotConnected()
{
    signConnected();
}

void TcpClient::slotDisconnected()
{
    signDisconnected();
}

//void TcpClient::displayError(QAbstractSocket::SocketError err)
//{
//    signDisplayError(err);
//}

void TcpClient::save2File(TcpProtocol tcpProtocol)
{
    if(tcpProtocol.seek == 0){
        m_file.close();
        m_file.setFileName("./"+m_saveFileName);
        m_file.open(QFile::WriteOnly);
    }

    m_file.seek(tcpProtocol.seek);
    qDebug()<<"recive data seek = "<<tcpProtocol.seek<<"tcpProtocol.data.size() + tcpProtocol.seek"<<tcpProtocol.data.size()+tcpProtocol.seek;
    m_file.write(tcpProtocol.data.data(),tcpProtocol.data.size());

    if(tcpProtocol.isLastPack == 1){
        m_file.close();
        qDebug()<<"file save sucess";
    }
}

void TcpClient::sendReadFileData(TcpProtocol tcpProtocol, int num)
{
    TcpProtocol sendtcpProtocol;
    sendtcpProtocol.cmd = 3;
    if(num == -1){
        sendtcpProtocol.seek = 0;
    }else{
        sendtcpProtocol.seek = tcpProtocol.seek + tcpProtocol.data.size();
        qDebug()<<"tcpProtocol.data.size() = "<<tcpProtocol.data.size();
        qDebug()<<"tcpProtocol.seek = "<<tcpProtocol.seek;
    }

    QByteArray arr = sendtcpProtocol.getStream();
    m_socket->write(arr);
    m_socket->waitForBytesWritten();
}
void TcpClient::sendFile(const QString &filepath, int seek)
{
    m_fileName = filepath;
    startTransfer();
}

void TcpClient::startTransfer()
{
    m_localFile=new QFile(m_fileName);
    if(!m_localFile->open(QFile::ReadOnly)){
        qDebug()<<"client：open file error!";
        return;
    }
    m_totalBytes=m_localFile->size();
    QDataStream sendOut(&m_outBlock,QIODevice::WriteOnly);
    sendOut.setVersion(QDataStream::Qt_5_7);
    QString currentFileName=m_fileName.right(m_fileName.size()-m_fileName.lastIndexOf('/')-1);
    //文件总大小、文件名大小、文件名
    sendOut<<qint64(0)<<qint64(0)<<currentFileName;
    m_totalBytes+=m_outBlock.size();
    sendOut.device()->seek(0);
    sendOut<<m_totalBytes<<qint64(m_outBlock.size()-sizeof(qint64)*2);
    m_bytesToWrite=m_totalBytes-m_filesocket->write(m_outBlock);
    m_outBlock.resize(0);

}

void TcpClient::updateClientProgress(qint64 numBytes)
{
    m_bytesWritten+=(int)numBytes;
    if(m_bytesToWrite>0){
        m_outBlock=m_localFile->read(qMin(m_bytesToWrite,m_payloadSize));
        m_bytesToWrite-=(int)m_filesocket->write(m_outBlock);
        m_outBlock.resize(0);
        qDebug()<<__FUNCTION__<<"numBytes = "<<numBytes;
    }else{
        m_localFile->close();
    }
    if(m_bytesWritten==m_totalBytes){
        m_localFile->close();
    }

}

